<script setup lang="ts">
import { ref, computed, reactive, onMounted, nextTick } from 'vue'

interface Props {
  data: any
  colorScheme: Record<string, string>
  maxDepth?: number
  virtualScroll?: boolean
  itemHeight?: number
}

const props = withDefaults(defineProps<Props>(), {
  maxDepth: 10,
  virtualScroll: true,
  itemHeight: 24
})

interface TreeNode {
  key?: string
  value: any
  type: 'object' | 'array' | 'string' | 'number' | 'boolean' | 'null'
  level: number
  path: string
  collapsed: boolean
  hasChildren: boolean
  childCount?: number
  visible: boolean
  index: number
}

const collapsedNodes = reactive(new Set<string>())
const containerRef = ref<HTMLElement>()
const scrollTop = ref(0)
const containerHeight = ref(400)
const visibleStartIndex = ref(0)
const visibleEndIndex = ref(50)
const overscan = 5

// 构建扁平化的树节点列表
const flatNodes = computed(() => {
  const nodes: TreeNode[] = []
  let index = 0
  
  function traverse(value: any, key?: string, level = 0, path = '', parentVisible = true): void {
    if (level > props.maxDepth) return
    
    const nodeType = getValueType(value)
    const hasChildren = nodeType === 'object' || nodeType === 'array'
    const isCollapsed = collapsedNodes.has(path)
    const isVisible = parentVisible && (level === 0 || !collapsedNodes.has(getParentPath(path)))
    
    const node: TreeNode = {
      key,
      value,
      type: nodeType,
      level,
      path,
      collapsed: isCollapsed,
      hasChildren,
      childCount: hasChildren ? getChildCount(value) : 0,
      visible: isVisible,
      index: index++
    }
    
    if (isVisible) {
      nodes.push(node)
    }
    
    // 如果节点未折叠且有子节点，递归处理子节点
    if (hasChildren && !isCollapsed && isVisible) {
      if (nodeType === 'array') {
        value.forEach((item: any, i: number) => {
          traverse(item, undefined, level + 1, `${path}[${i}]`, true)
        })
      } else if (nodeType === 'object') {
        Object.entries(value).forEach(([k, v]) => {
          traverse(v, k, level + 1, `${path}.${k}`, true)
        })
      }
    }
  }
  
  traverse(props.data)
  return nodes
})

// 虚拟滚动相关计算
const visibleNodes = computed(() => {
  if (!props.virtualScroll) {
    return flatNodes.value
  }
  
  const start = Math.max(0, visibleStartIndex.value - overscan)
  const end = Math.min(flatNodes.value.length, visibleEndIndex.value + overscan)
  
  return flatNodes.value.slice(start, end).map((node, index) => ({
    ...node,
    virtualIndex: start + index,
    offsetTop: (start + index) * props.itemHeight
  }))
})

const totalHeight = computed(() => flatNodes.value.length * props.itemHeight)

function getValueType(value: any): TreeNode['type'] {
  if (value === null) return 'null'
  if (Array.isArray(value)) return 'array'
  if (typeof value === 'object') return 'object'
  return typeof value as TreeNode['type']
}

function getChildCount(value: any): number {
  if (Array.isArray(value)) return value.length
  if (typeof value === 'object' && value !== null) return Object.keys(value).length
  return 0
}

function getParentPath(path: string): string {
  const lastDotIndex = path.lastIndexOf('.')
  const lastBracketIndex = path.lastIndexOf('[')
  const lastIndex = Math.max(lastDotIndex, lastBracketIndex)
  
  if (lastIndex === -1) return ''
  if (path[lastIndex] === '[') {
    return path.substring(0, lastIndex)
  }
  return path.substring(0, lastIndex)
}

function toggleCollapse(path: string) {
  if (collapsedNodes.has(path)) {
    collapsedNodes.delete(path)
  } else {
    collapsedNodes.add(path)
  }
}

function getValuePreview(value: any, type: TreeNode['type']): string {
  switch (type) {
    case 'string':
      return value.length > 50 ? `"${value.substring(0, 50)}..."` : `"${value}"`
    case 'array':
      return `Array(${value.length})`
    case 'object':
      const keys = Object.keys(value)
      return `Object(${keys.length})`
    case 'null':
      return 'null'
    default:
      return String(value)
  }
}

function getValueColor(type: TreeNode['type']): string {
  switch (type) {
    case 'string': return props.colorScheme.string
    case 'number': return props.colorScheme.number
    case 'boolean': return props.colorScheme.boolean
    case 'null': return props.colorScheme.null
    default: return props.colorScheme.punctuation
  }
}

// 虚拟滚动处理
function handleScroll(event: Event) {
  const target = event.target as HTMLElement
  scrollTop.value = target.scrollTop
  
  const start = Math.floor(scrollTop.value / props.itemHeight)
  const visibleCount = Math.ceil(containerHeight.value / props.itemHeight)
  
  visibleStartIndex.value = start
  visibleEndIndex.value = start + visibleCount
}

// 展开所有节点
function expandAll() {
  collapsedNodes.clear()
}

// 折叠所有节点
function collapseAll() {
  flatNodes.value.forEach(node => {
    if (node.hasChildren && node.level > 0) {
      collapsedNodes.add(node.path)
    }
  })
}

// 复制路径
async function copyPath(path: string) {
  try {
    await navigator.clipboard.writeText(path)
  } catch {
    // 静默失败
  }
}

onMounted(() => {
  if (containerRef.value) {
    containerHeight.value = containerRef.value.clientHeight
  }
})
</script>

<template>
  <div class="json-tree-view">
    <!-- 工具栏 -->
    <div class="flex items-center justify-between mb-2 text-xs">
      <div class="flex items-center space-x-2">
        <button 
          @click="expandAll"
          class="px-2 py-1 bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300 rounded hover:bg-blue-200 dark:hover:bg-blue-800 transition-colors"
        >
          <i class="fa fa-expand mr-1"></i> 全部展开
        </button>
        <button 
          @click="collapseAll"
          class="px-2 py-1 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors"
        >
          <i class="fa fa-compress mr-1"></i> 全部折叠
        </button>
      </div>
      <div class="text-gray-500 dark:text-gray-400">
        共 {{ flatNodes.length }} 个节点
      </div>
    </div>

    <!-- 树形视图容器 -->
    <div 
      ref="containerRef"
      class="relative overflow-auto border border-gray-200 dark:border-gray-600 rounded"
      :style="{ height: virtualScroll ? '400px' : 'auto', maxHeight: '400px' }"
      @scroll="handleScroll"
    >
      <!-- 虚拟滚动容器 -->
      <div v-if="virtualScroll" :style="{ height: totalHeight + 'px', position: 'relative' }">
        <div
          v-for="node in visibleNodes"
          :key="node.path"
          :style="{ 
            position: 'absolute',
            top: node.offsetTop + 'px',
            left: '0',
            right: '0',
            height: itemHeight + 'px'
          }"
          class="flex items-center px-2 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors group"
        >
          <!-- 缩进 -->
          <div :style="{ width: (node.level * 16) + 'px' }" class="flex-shrink-0"></div>
          
          <!-- 折叠/展开按钮 -->
          <button
            v-if="node.hasChildren"
            @click="toggleCollapse(node.path)"
            class="w-4 h-4 flex items-center justify-center text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors mr-1"
          >
            <i :class="node.collapsed ? 'fa fa-caret-right' : 'fa fa-caret-down'" class="text-xs"></i>
          </button>
          <div v-else class="w-4 mr-1"></div>
          
          <!-- 键名 -->
          <span 
            v-if="node.key !== undefined"
            :style="{ color: colorScheme.key }"
            class="font-medium mr-1"
          >
            "{{ node.key }}"
          </span>
          <span v-if="node.key !== undefined" :style="{ color: colorScheme.punctuation }" class="mr-1">:</span>
          
          <!-- 值 -->
          <span 
            :style="{ color: getValueColor(node.type) }"
            class="flex-1 truncate"
            :title="getValuePreview(node.value, node.type)"
          >
            {{ getValuePreview(node.value, node.type) }}
          </span>
          
          <!-- 子元素数量 -->
          <span 
            v-if="node.hasChildren && node.collapsed"
            class="text-xs text-gray-400 ml-2"
          >
            {{ node.childCount }} 项
          </span>
          
          <!-- 复制路径按钮 -->
          <button
            @click="copyPath(node.path)"
            class="opacity-0 group-hover:opacity-100 ml-2 text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-all"
            title="复制路径"
          >
            <i class="fa fa-copy"></i>
          </button>
        </div>
      </div>

      <!-- 非虚拟滚动模式 -->
      <div v-else>
        <div
          v-for="node in flatNodes"
          :key="node.path"
          class="flex items-center px-2 py-1 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors group"
        >
          <!-- 缩进 -->
          <div :style="{ width: (node.level * 16) + 'px' }" class="flex-shrink-0"></div>
          
          <!-- 折叠/展开按钮 -->
          <button
            v-if="node.hasChildren"
            @click="toggleCollapse(node.path)"
            class="w-4 h-4 flex items-center justify-center text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors mr-1"
          >
            <i :class="node.collapsed ? 'fa fa-caret-right' : 'fa fa-caret-down'" class="text-xs"></i>
          </button>
          <div v-else class="w-4 mr-1"></div>
          
          <!-- 键名 -->
          <span 
            v-if="node.key !== undefined"
            :style="{ color: colorScheme.key }"
            class="font-medium mr-1"
          >
            "{{ node.key }}"
          </span>
          <span v-if="node.key !== undefined" :style="{ color: colorScheme.punctuation }" class="mr-1">:</span>
          
          <!-- 值 -->
          <span 
            :style="{ color: getValueColor(node.type) }"
            class="flex-1 truncate"
            :title="getValuePreview(node.value, node.type)"
          >
            {{ getValuePreview(node.value, node.type) }}
          </span>
          
          <!-- 子元素数量 -->
          <span 
            v-if="node.hasChildren && node.collapsed"
            class="text-xs text-gray-400 ml-2"
          >
            {{ node.childCount }} 项
          </span>
          
          <!-- 复制路径按钮 -->
          <button
            @click="copyPath(node.path)"
            class="opacity-0 group-hover:opacity-100 ml-2 text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-all"
            title="复制路径"
          >
            <i class="fa fa-copy"></i>
          </button>
        </div>
      </div>
    </div>
  </div>
</template>



<style scoped>
.json-tree-view {
  font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
  font-size: 13px;
  line-height: 1.4;
}

/* 滚动条样式 */
.overflow-auto::-webkit-scrollbar {
  width: 6px;
  height: 6px;
}

.overflow-auto::-webkit-scrollbar-track {
  background: transparent;
}

.overflow-auto::-webkit-scrollbar-thumb {
  background: rgba(156, 163, 175, 0.5);
  border-radius: 3px;
}

.overflow-auto::-webkit-scrollbar-thumb:hover {
  background: rgba(156, 163, 175, 0.7);
}

.dark .overflow-auto::-webkit-scrollbar-thumb {
  background: rgba(75, 85, 99, 0.5);
}

.dark .overflow-auto::-webkit-scrollbar-thumb:hover {
  background: rgba(75, 85, 99, 0.7);
}
</style>